/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.coreGenerator.base.accessorgen.method;

import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.DeleteChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.common.MediatePropertyInfo;
import com.inspur.edp.cef.generator.coreGenerator.base.accessorgen.AccessorClassGenContext;
import java.util.List;

import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;
import java.util.Map;

public class ChildAccessorAcceptChangeGen extends ClassMethodGenerator
{
	private static final String ParmChange = "change";
	private static final String ParentVarName = "Parent";
	private AccessorClassGenContext parentContext;
	private List<MediatePropertyInfo> dataType;
	private MediateType targetInterfaceType;
	private  static  String ParamVar="var";
	private String nodeCode;
	private static String ParamPropertyChange="propertyChange";

	public ChildAccessorAcceptChangeGen(AccessorClassGenContext context, List<MediatePropertyInfo> dataType, MediateType targetInterfaceType, String nodeCode)
	{
		this.parentContext = context;
		this.dataType = dataType;
		this.targetInterfaceType = targetInterfaceType;
		this.nodeCode = nodeCode;
	}
	@Override
	protected String getMethodName(){return "acceptChangeCore";}


	@Override
	protected TypeInfo getReturnType() {
		return new TypeInfo(void.class.getTypeName());
	}

	@Override
	protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
		ArrayList<Modifier.ModifierKeyword> list =new ArrayList<>();
		list.add(Modifier.ModifierKeyword.PROTECTED_KEYWORD);
		return list;
	}

	@Override
	protected boolean getIsOverride() {
		return true;
	}

	@Override
	protected ArrayList<ParameterInfo> getParameterCollection() {
		ArrayList<ParameterInfo> list=new ArrayList<ParameterInfo>();
		ParameterInfo parameterInfo=new ParameterInfo();
		parameterInfo.setParamType(new TypeInfo(IChangeDetail.class.getTypeName()));
		parameterInfo.setParamName(ParmChange);
		list.add(parameterInfo);
		return list;
	}

	@Override
	protected Block buildMethodBody() {
		Block block=ast.newBlock();
		addSuperStatement(block);
		addProcessModifyChangeStatements(block);
		addProcessDeleteChangeStatements(block);
		addProcessAddChangeStatements(block);
		addThrowStatements(block);

		return block;
	}

	private void addSuperStatement(Block block) {
		SuperMethodInvocation superInvocation = ast.newSuperMethodInvocation();
		superInvocation.setName(ast.newSimpleName(getMethodName()));
		superInvocation.arguments().add(ast.newSimpleName(ParmChange));
		block.statements().add(ast.newExpressionStatement(superInvocation));
	}

	private void addProcessModifyChangeStatements(Block block)
	{
		addProcessModifyCastModifyChangeStatements(block);
		addProcessModifyPropertiesStatements(block);
	}

	private void addProcessModifyCastModifyChangeStatements(Block block) {
		VariableDeclarationFragment variableDeclarationFragment=ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName(ParamVar));

		variableDeclarationFragment.setInitializer(ExpressionUtils
				.getAsExpression(ast, ast -> ast.newSimpleName(ParmChange),
						ast -> new TypeInfo(ModifyChangeDetail.class).getType(ast)));


		VariableDeclarationStatement variableDeclarationStatement=ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(new TypeInfo(ModifyChangeDetail.class).getType(ast));
		block.statements().add(variableDeclarationStatement);
	}

	private void addProcessModifyPropertiesStatements(Block block) {
		IfStatement ifStatement=ast.newIfStatement();
		addModifyIfExpression(ifStatement);
		addModifyIfThen(ifStatement);
		block.statements().add(ifStatement);
	}


	private void addModifyIfExpression(IfStatement ifStatement) {
		InfixExpression infixExpression=ast.newInfixExpression();
		infixExpression.setLeftOperand(ast.newSimpleName(ParamVar));
		infixExpression.setOperator(InfixExpression.Operator.NOT_EQUALS);
		infixExpression.setRightOperand(ast.newNullLiteral());
		ifStatement.setExpression(infixExpression);

	}

	private void addModifyIfThen(IfStatement ifStatement)
	{
		Block ifThenBlock =ast.newBlock();
		addSwitchModifyProperties(ifThenBlock);
		ifStatement.setThenStatement(ifThenBlock);
		AccessorAcceptChangeGen.addSetChangeChildsValuesStatement(ast, ifThenBlock);
		addProcessModifyChangeReturn(ifThenBlock);
	}
	private void addSwitchModifyProperties(Block ifThenBlock) {
		EnhancedForStatement forStatement=ast.newEnhancedForStatement();
		addSetChangeProForParameter(forStatement);
		addSetChangeProForExpression(forStatement);
		addSetChangeProForBody(forStatement);
		ifThenBlock.statements().add(forStatement);
	}
	private void addSetChangeProForParameter(EnhancedForStatement forStatement)
	{
		SingleVariableDeclaration singleVariableDeclaration =ast.newSingleVariableDeclaration();
		singleVariableDeclaration.setName(ast.newSimpleName(ParamPropertyChange));
		ParameterizedType parameterizedType = ast.newParameterizedType(ast.newSimpleType(ast.newQualifiedName(ast.newName(Map.class.getTypeName()),ast.newSimpleName("Entry"))));
		parameterizedType.typeArguments().add(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(String.class).getType(ast));
		parameterizedType.typeArguments().add(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(Object.class).getType(ast));
		singleVariableDeclaration.setType(parameterizedType);

		forStatement.setParameter(singleVariableDeclaration);
	}
	private void addSetChangeProForExpression(EnhancedForStatement forStatement) {
		MethodInvocation methodInvocation=ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("entrySet"));

		MethodInvocation methodInvocation1=ast.newMethodInvocation();
		methodInvocation1.setName(ast.newSimpleName("getPropertyChanges"));
		methodInvocation1.setExpression(ast.newSimpleName(ParamVar));
		methodInvocation.setExpression(methodInvocation1);

		forStatement.setExpression(methodInvocation);
	}

	private void addSetChangeProForBody(EnhancedForStatement forStatement) {
		Block block=ast.newBlock();
		SwitchStatement switchStatement =ast.newSwitchStatement();
		addSetChangeProForSwitchExpression(switchStatement);
		addSetChangeProCases(switchStatement);
		block.statements().add(switchStatement);
		forStatement.setBody(block);
	}

	private void addSetChangeProForSwitchExpression(SwitchStatement switchStatement) {
	MethodInvocation methodInvocation=ast.newMethodInvocation();
	methodInvocation.setExpression(ast.newSimpleName(ParamPropertyChange));
	methodInvocation.setName(ast.newSimpleName("getKey"));
	switchStatement.setExpression(methodInvocation);
}

	private void addSetChangeProCases(SwitchStatement switchStatement)
	{

		for(MediatePropertyInfo field:dataType)
		{
		AccessorAcceptChangeGen.addSetChangeProCase(ast,nodeCode,field,switchStatement,parentContext);
		}
	}

	private void addProcessModifyChangeReturn(Block ifThenBlock) {
		ifThenBlock.statements().add(ast.newReturnStatement());
	}



	private void addProcessDeleteChangeStatements(Block block)
	{
		addProcessDeleteChangeNewDeleteChange(block);
		addProcessDeleteChangeIf(block);
	}

	private void addProcessDeleteChangeNewDeleteChange(Block block) {
		VariableDeclarationFragment variableDeclarationFragment=ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName("val3"));

		variableDeclarationFragment.setInitializer(ExpressionUtils
				.getAsExpression(ast, ast -> ast.newSimpleName(ParmChange),
						ast -> new TypeInfo(DeleteChangeDetail.class).getType(ast)));

		VariableDeclarationStatement variableDeclarationStatement=ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(DeleteChangeDetail.class).getType(ast));
		block.statements().add(variableDeclarationStatement);
	}

	private void addProcessDeleteChangeIf(Block block)
	{
		IfStatement ifStatement=ast.newIfStatement();
		addProcessDeleteChangeIfExpression(ifStatement);
		addProcessDeleteChangeIfThen(ifStatement);
		block.statements().add(ifStatement);
	}

	private void addProcessDeleteChangeIfExpression(IfStatement ifStatement) {
		InfixExpression infixExpression=ast.newInfixExpression();
		infixExpression.setLeftOperand(ast.newSimpleName("val3"));
		infixExpression.setOperator(InfixExpression.Operator.NOT_EQUALS);
		infixExpression.setRightOperand(ast.newNullLiteral());
		ifStatement.setExpression(infixExpression);
	}

	private void addProcessDeleteChangeIfThen(IfStatement ifStatement)
	{
		Block block=ast.newBlock();
		addProcessDeleteChangeIfThenIf(block);
		addProcessDeleteChangeIfThenReturn(block);
		ifStatement.setThenStatement(block);
	}

	private void addProcessDeleteChangeIfThenIf(Block block) {
		IfStatement ifStatement=ast.newIfStatement();
		addProcessDeleteChangeIfThenIfExpression(ifStatement);
		addProcessDeleteChangeIfThenIfThen(ifStatement);

		block.statements().add(ifStatement);
	}

	private void addProcessDeleteChangeIfThenIfThen(IfStatement ifStatement) {
		Block block=ast.newBlock();
		MethodInvocation methodInvocation1=ast.newMethodInvocation();
		methodInvocation1.setName(ast.newSimpleName("remove"));
		addProcessDeleteChangeIfThenIfThenMethodInvokeExpression(methodInvocation1);
		addProcessDeleteChangeIfThenIfThenMethodInvokeArgument(methodInvocation1);

		ExpressionStatement expressionStatement=ast.newExpressionStatement(methodInvocation1);
		block.statements().add(expressionStatement);
		ifStatement.setThenStatement(block);
	}


	private void addProcessDeleteChangeIfThenIfThenMethodInvokeExpression(MethodInvocation methodInvocationRemove) {
		MethodInvocation methodInvocationGetItems=ast.newMethodInvocation();
		methodInvocationGetItems.setName(ast.newSimpleName("get"+nodeCode+"s"));

		MethodInvocation methodInvocationGetParent =ast.newMethodInvocation();
		methodInvocationGetParent.setName(ast.newSimpleName("getParent"));
		methodInvocationGetParent.setExpression(ast.newThisExpression());
		CastExpression castExpression =ast.newCastExpression();
		castExpression.setExpression(methodInvocationGetParent);
		castExpression.setType(ast.newSimpleType(ast.newName(targetInterfaceType.getFullName())));
		ParenthesizedExpression parenthesizedExpression = ast.newParenthesizedExpression();
		parenthesizedExpression.setExpression(castExpression);

		methodInvocationGetItems.setExpression(parenthesizedExpression);
		methodInvocationRemove.setExpression(methodInvocationGetItems);
	}

	//this.getParent().getSalesOrderItems().Remove(getID()) 中的 getID()
	private void addProcessDeleteChangeIfThenIfThenMethodInvokeArgument(MethodInvocation methodInvocation1)
	{
		MethodInvocation methodInvocationGetID=ast.newMethodInvocation();
		methodInvocationGetID.setName(ast.newSimpleName("getID"));
		methodInvocation1.arguments().add(methodInvocationGetID);
	}

	private void addProcessDeleteChangeIfThenIfExpression(IfStatement ifStatement) {
		InfixExpression infixExpression=ast.newInfixExpression();

		MethodInvocation methodInvocation1=ast.newMethodInvocation();
		methodInvocation1.setExpression(ast.newThisExpression());
		methodInvocation1.setName(ast.newSimpleName("getParent"));

		infixExpression.setLeftOperand(methodInvocation1);
		infixExpression.setOperator(InfixExpression.Operator.NOT_EQUALS);
		infixExpression.setRightOperand(ast.newNullLiteral());
		ifStatement.setExpression(infixExpression);
	}

	//return;
	private void addProcessDeleteChangeIfThenReturn(Block block) {
		block.statements().add(ast.newReturnStatement());
	}


	private void addProcessAddChangeStatements(Block block)
	{
		addProcessAddChangeStatementsNewAdd(block);
		addProcessAddChangeStatementsIf(block);
	}

	//AddChangeDetail val4 = change as AddChangeDetail;
	private void addProcessAddChangeStatementsNewAdd(Block block) {
		VariableDeclarationFragment variableDeclarationFragment=ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName("val4"));

		variableDeclarationFragment.setInitializer(ExpressionUtils
				.getAsExpression(ast, ast -> ast.newSimpleName(ParmChange),
						ast -> new TypeInfo(AddChangeDetail.class).getType(ast)));

		VariableDeclarationStatement variableDeclarationStatement=ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(AddChangeDetail.class).getType(ast));
		block.statements().add(variableDeclarationStatement);
	}

	/*if (val4 != null)
		{
			if (this.getParent() != null)
			{
				this.getParent().get_SalesOrderItems().add(val4.getEntityData().copy());
			}
			return;
		}*/
	private void addProcessAddChangeStatementsIf(Block block) {
		IfStatement ifStatement=ast.newIfStatement();
		addProcessAddChangeStatementsIfExpression(ifStatement);
		addProcessAddChangeStatementsIfThen(ifStatement);
		block.statements().add(ifStatement);
	}

	//val4 != null
	private void addProcessAddChangeStatementsIfExpression(IfStatement ifStatement) {
		InfixExpression infixExpression=ast.newInfixExpression();
		infixExpression.setLeftOperand(ast.newSimpleName("val4"));
		infixExpression.setOperator(InfixExpression.Operator.NOT_EQUALS);
		infixExpression.setRightOperand(ast.newNullLiteral());
		ifStatement.setExpression(infixExpression);
	}

	/*if (this.getParent() != null)
			{
				this.getParent().get_SalesOrderItems().add(val4.getEntityData().copy());
			}
			return;*/
	private void addProcessAddChangeStatementsIfThen(IfStatement ifStatement)
	{
		Block block=ast.newBlock();
		addProcessAddChangeStatementsIfThengetParentIf(block);
		addProcessAddChangeStatementsIfThenReturn(block);
		ifStatement.setThenStatement(block);
	}

	private void addProcessAddChangeStatementsIfThengetParentIf(Block block) {
		IfStatement ifStatement=ast.newIfStatement();
		addProcessAddChangeStatementsIfThengetParentIfExpression(ifStatement);
		addProcessAddChangeStatementsIfThengetParentIfThen(ifStatement);
		block.statements().add(ifStatement);
	}

	//this.getParent() != null
	private void addProcessAddChangeStatementsIfThengetParentIfExpression(IfStatement ifStatement) {
		InfixExpression infixExpression=ast.newInfixExpression();
		MethodInvocation methodInvocation1=ast.newMethodInvocation();
		methodInvocation1.setExpression(ast.newThisExpression());
		methodInvocation1.setName(ast.newSimpleName("getParent"));
		infixExpression.setLeftOperand(methodInvocation1);
		infixExpression.setOperator(InfixExpression.Operator.NOT_EQUALS);
		infixExpression.setRightOperand(ast.newNullLiteral());

		ifStatement.setExpression(infixExpression);

	}

	//this.getParent().get_SalesOrderItems().add(val4.getEntityData().copy());
	private void addProcessAddChangeStatementsIfThengetParentIfThen(IfStatement ifStatement)
	{
		Block block=ast.newBlock();
		MethodInvocation methodInvocationAdd=ast.newMethodInvocation();
		methodInvocationAdd.setName(ast.newSimpleName("add"));
		addProcessAddChangeStatementsIfThengetParentIfThenMethodAddExpression(methodInvocationAdd);
		addProcessAddChangeStatementsIfThengetParentIfThenMethodAddArguments(methodInvocationAdd);

		ExpressionStatement expressionStatement=ast.newExpressionStatement(methodInvocationAdd);
		block.statements().add(expressionStatement);
		ifStatement.setThenStatement(block);
	}

	//this.getParent().get_SalesOrderItems()
	private void addProcessAddChangeStatementsIfThengetParentIfThenMethodAddExpression(MethodInvocation methodInvocationAdd) {
		MethodInvocation methodGetItems = ast.newMethodInvocation();
		methodGetItems.setName(ast.newSimpleName("get"+nodeCode+"s"));

		MethodInvocation methodInvocationGetParent=ast.newMethodInvocation();
		methodInvocationGetParent.setName(ast.newSimpleName("getParent"));
		methodInvocationGetParent.setExpression(ast.newThisExpression());

		CastExpression castExpression =ast.newCastExpression();
		castExpression.setExpression(methodInvocationGetParent);
		castExpression.setType(ast.newSimpleType(ast.newName(targetInterfaceType.getFullName())));
		ParenthesizedExpression parenthesizedExpression = ast.newParenthesizedExpression();
		parenthesizedExpression.setExpression(castExpression);

		methodGetItems.setExpression(parenthesizedExpression);
		methodInvocationAdd.setExpression(methodGetItems);
	}

	//val4.getEntityData().copy()
	private void addProcessAddChangeStatementsIfThengetParentIfThenMethodAddArguments(MethodInvocation methodInvocationAdd) {

		MethodInvocation methodInvocationCopy =ast.newMethodInvocation();
		methodInvocationCopy.setName(ast.newSimpleName("copy"));

		MethodInvocation methodInvocationGetEntityData =ast.newMethodInvocation();
		methodInvocationGetEntityData.setName(ast.newSimpleName("getEntityData"));
		methodInvocationGetEntityData.setExpression(ast.newSimpleName("val4"));
		methodInvocationCopy.setExpression(methodInvocationGetEntityData);

		CastExpression castEntityData = ast.newCastExpression();
		castEntityData.setType(new TypeInfo(IEntityData.class).getType(ast));
		castEntityData.setExpression(methodInvocationCopy);

		methodInvocationAdd.arguments().add(castEntityData);
	}

	//return;
	private void addProcessAddChangeStatementsIfThenReturn(Block block) {
		block.statements().add(ast.newReturnStatement());
	}

	//throw new RuntimeException();
	private void addThrowStatements(Block block)
	{
//		SetExceptionCode setExceptionCode =new SetExceptionCode();
//		ClassInstanceCreation classInstanceCreation=ast.newClassInstanceCreation();
//		setExceptionCode.setClassInstanceCreation(classInstanceCreation,ast,"","");
		//classInstanceCreation.setType(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(RuntimeException.class).getType(ast));
		addImport(CefException.class.getTypeName());
		ThrowStatement throwStatement= GeneratorUtil.getThrowStatement(ast,null);
		block.statements().add(throwStatement);
	}
}
